package com.example.oom;

import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;

import java.util.ArrayList;
import java.util.List;

/**
 * 使用 ASM 动态生成类，触发 Metaspace OOM。
 * 
 * 运行示例：
 *   javac -cp asm-9.6.jar MetaspaceOOMWithASM.java
 *   java -cp .:asm-9.6.jar -XX:MaxMetaspaceSize=64m MetaspaceOOMWithASM
 */
public class MetaspaceOOMWithASM extends ClassLoader implements Opcodes {

    public static void main(String[] args) {
        System.out.println("开始模拟 java.lang.OutOfMemoryError: Metaspace ...");
        System.out.println("建议使用参数：-XX:MaxMetaspaceSize=64m");

        // 用来强引用所有的 ClassLoader，防止被 GC
        List<ClassLoader> keep = new ArrayList<>();
        int count = 0;

        try {
            while (true) {
                // 每轮生成一个新类
                String className = "demo.Generated" + count;
                byte[] classBytes = generateClassBytes(className);

                // 每次使用一个新的 ClassLoader 加载
                MetaspaceOOMWithASM loader = new MetaspaceOOMWithASM();
                loader.defineClass(className, classBytes, 0, classBytes.length);
                keep.add(loader); // 关键：防止 GC

                if (++count % 1000 == 0) {
                    System.out.println("已加载类数量: " + count);
                }
            }
        } catch (Throwable e) {
            e.printStackTrace();
            System.err.println("在加载 " + count + " 个类后抛出异常！");
        }
    }

    /**
     * 使用 ASM 生成最简单的类：
     * public class demo.GeneratedN {
     *     public void hello() {}
     * }
     */
    private static byte[] generateClassBytes(String className) {
        ClassWriter cw = new ClassWriter(0);
        cw.visit(V1_8, ACC_PUBLIC, className.replace('.', '/'),
                null, "java/lang/Object", null);

        // 默认构造函数
        MethodVisitor mv = cw.visitMethod(ACC_PUBLIC, "<init>", "()V", null, null);
        mv.visitVarInsn(ALOAD, 0);
        mv.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "<init>", "()V", false);
        mv.visitInsn(RETURN);
        mv.visitMaxs(1, 1);
        mv.visitEnd();

        // 简单方法 hello()
        mv = cw.visitMethod(ACC_PUBLIC, "hello", "()V", null, null);
        mv.visitInsn(RETURN);
        mv.visitMaxs(0, 1);
        mv.visitEnd();

        cw.visitEnd();
        return cw.toByteArray();
    }
}
